#include <iostream>
#include <cmath>
#include <fstream>
#include "pin.H"
#include <assert.h>
#include "cachesim/singlecache_wrapper.H"
#include "cachesim/Cache.h"

ofstream OutFile;

//#define PRINT_INSTRUCTION
//#define DEBUG_REGISTER 

//#define CACHE_SIM_ENABLED
#define OTHER_SIM_ENABLED

// Global counters
namespace EE382M {
  typedef enum {
    INT,
    FP,
    LOAD,
    STORE,
    BRANCHnCALL,
    LOADSTORE,
    BRANCH,
    BRANCH_T,
    FORWARD_BR,
    FORWARD_BR_T, //taken forward branch instruction
    TOTAL,
    InstructionMixCount
  } InstructionMix;

  typedef enum {
    EAX,
    EBX,
    ECX,
    EDX,
    ESI,
    EDI,
    TOTALREGS
  } Registers;
}

string InsString[EE382M::InstructionMixCount] = {
  "INT",
  "FP",
  "LOAD",
  "STORE",
  "BRANCHnCALL",
  "LOADSTORE",
  "BRANCH",
  "BRANCH_T",
  "FORWARD_BR",
  "FORWARD_BR_T", //taken forward branch instruction
  "TOTAL",
};

string RegistersPrint[EE382M::TOTALREGS] = {
    "EAX",
    "EBX",
    "ECX",
    "EDX",
    "ESI",
    "EDI"
};

// instruction mix
static UINT64 instr_mix[EE382M::InstructionMixCount] = {0};

// BB size
std::vector<USIZE> BBL_size_container_static;
//std::vector<USIZE> BBL_size_container_dynamic;
UINT32 BBL_index_global = 0;
std::vector<USIZE> BBL_size_count;


// Register Dependency
UINT32 RegDepRAW[EE382M::TOTALREGS] = {0};
UINT64 RegDepRAW_recorder[6];
UINT32 RegDepWAR[EE382M::TOTALREGS] = {0};
UINT64 RegDepWAR_recorder[6];
UINT32 RegDepWAW[EE382M::TOTALREGS] = {0};
UINT64 RegDepWAW_recorder[6];

// density function
std::vector<ADDRINT> block_stack; // stack on the block granularity
UINT64 block_recorder[11];
std::vector<ADDRINT> page_stack; // stack on the page granularity
UINT64 page_recorder[11];

// Cache Simulator
SingleCacheWrapper cache_sim;
unsigned long long current_cycle = 0;
//if multiple memory requests happen in the same isntruction, this is going to hold the request
typedef struct MemRequest {
	unsigned long long cycle;
	addr_type address;
	bool is_store;

	MemRequest(unsigned long long c, ADDRINT addr, bool is_s) {
		cycle = c;
		address = addr;
		is_store = is_s;
	}

} MemRequest;
std::vector<MemRequest> mem_req_buffer;
unsigned long long tot_reqs = 0;

/************************* Instruction Mix ************************/
VOID docount() {
  instr_mix[EE382M::TOTAL] ++;
}

VOID IncBranchnCall() {
  instr_mix[EE382M::BRANCHnCALL] ++;
}

VOID IncBranch() {
  instr_mix[EE382M::BRANCH] ++;
}

VOID IncBranch_T() {
  instr_mix[EE382M::BRANCH_T] ++;
}

VOID IncForwardBr() {
  instr_mix[EE382M::FORWARD_BR] ++;
}

VOID IncForwardBrT() {
  instr_mix[EE382M::FORWARD_BR_T] ++;
}

VOID IncLoad() {
  instr_mix[EE382M::LOAD] ++;
}

VOID IncStore() {
  instr_mix[EE382M::STORE] ++;
}

VOID IncLoadStore() {
  instr_mix[EE382M::LOADSTORE] ++;
}

VOID IncFP() {
  instr_mix[EE382M::FP] ++;
}

VOID IncINT() {
  instr_mix[EE382M::INT] ++;
}
/************************* Instruction Mix ************************/

/************************* BranchBehavior ************************/
VOID branch_behavior(ADDRINT ins_ptr, ADDRINT target_addr, INT taken) {
  IncBranch();
  if (taken) {
    IncBranch_T();
  }
  if (target_addr > ins_ptr) {
    IncForwardBr();
    if (taken) IncForwardBrT();
  }
}
/************************* BranchBehavior ************************/

/************************* Basic Block Size ************************/
VOID BBLInstrument ( UINT32 index, UINT32 size) {
    BBL_size_count[index] ++;
	//BBL_size_container_dynamic.push_back(size);
}
/************************* Basic Block Size ************************/

/************************* Register Depedency ************************/
VOID RegDependency (UINT32 regs_read, UINT32 regs_written) {

  // increase all register distance
  for ( UINT32 i = 0; i < EE382M::TOTALREGS; i ++ ) {
    RegDepRAW[i] ++;
    RegDepWAR[i] ++;
    RegDepWAW[i] ++;
  }

  // check read regs first
#ifdef DEBUG_REGISTER 
  printf ("\n------------------------\n");
#endif
  /************* RAW checking ****************/
#ifdef DEBUG_REGISTER 
  printf ("RAW checking: ");
#endif
  if ( regs_read != 0 ) {

#ifdef DEBUG_REGISTER 
    printf ("regs read: ");
#endif
    for (UINT32 i = 0; i < EE382M::TOTALREGS; i ++ ) {
      if ( regs_read & (0x1 << i)) {
#ifdef DEBUG_REGISTER 
        printf ("%s ", RegistersPrint[i].c_str());
#endif
        // if read, read this distance
        UINT32 distance = RegDepRAW[i];
        UINT32 tmp = 0;
        //printf ("distance %d\n", distance);

        if ( distance <= 2 ) 
          RegDepRAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 4 )
          RegDepRAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 8 )
          RegDepRAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 16 )
          RegDepRAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 32 )
          RegDepRAW_recorder[tmp]++;
        else
          RegDepRAW_recorder[tmp]++;
      }

    }
#ifdef DEBUG_REGISTER 
    printf("\n");
#endif
  }

  if ( regs_written != 0 ) {
#ifdef DEBUG_REGISTER 
    printf ("regs written: ");
#endif
    for (UINT32 i = 0; i < EE382M::TOTALREGS; i ++ ) {
      if ( regs_written & (0x1 << i)) {
#ifdef DEBUG_REGISTER 
        printf ("%s ", RegistersPrint[i].c_str());
#endif
        // reset the distance to written register to 0
        RegDepRAW[i] = 0;
      }

    }
#ifdef DEBUG_REGISTER 
    printf("\n");
#endif
  }

  /************* WAR checking ****************/
#ifdef DEBUG_REGISTER
  printf ("WAR checking: ");
#endif
  if ( regs_written != 0 ) {

#ifdef DEBUG_REGISTER
    printf ("regs written: ");
#endif
    for (UINT32 i = 0; i < EE382M::TOTALREGS; i ++ ) {
      if ( regs_written & (0x1 << i)) {
#ifdef DEBUG_REGISTER
        printf ("%s ", RegistersPrint[i].c_str());
#endif
        // if read, read this distance
        UINT32 distance = RegDepWAR[i];
        UINT32 tmp = 0;
        //printf ("distance %d\n", distance);

        if ( distance <= 2 )
          RegDepWAR_recorder[tmp]++;
        else if ( ++tmp && distance <= 4 )
          RegDepWAR_recorder[tmp]++;
        else if ( ++tmp && distance <= 8 )
          RegDepWAR_recorder[tmp]++;
        else if ( ++tmp && distance <= 16 )
          RegDepWAR_recorder[tmp]++;
        else if ( ++tmp && distance <= 32 )
          RegDepWAR_recorder[tmp]++;
        else
          RegDepWAR_recorder[tmp]++;
      }

    }
#ifdef DEBUG_REGISTER 
    printf("\n");
#endif
  }

  if ( regs_read != 0 ) {
#ifdef DEBUG_REGISTER 
    printf ("regs read: ");
#endif
    for (UINT32 i = 0; i < EE382M::TOTALREGS; i ++ ) {
      if ( regs_read & (0x1 << i)) {
#ifdef DEBUG_REGISTER 
        printf ("%s ", RegistersPrint[i].c_str());
#endif
      // reset the distance to written register to 0
      RegDepWAR[i] = 0;
      }

    }
#ifdef DEBUG_REGISTER 
    printf("\n");
#endif
  }

  /************* WAW checking ****************/
#ifdef DEBUG_REGISTER
  printf ("WAW checking: ");
#endif
  if ( regs_written != 0 ) {

#ifdef DEBUG_REGISTER
    printf ("regs written: ");
#endif
    for (UINT32 i = 0; i < EE382M::TOTALREGS; i ++ ) {
      if ( regs_written & (0x1 << i)) {
#ifdef DEBUG_REGISTER
        printf ("%s ", RegistersPrint[i].c_str());
#endif
        // if read, read this distance
        UINT32 distance = RegDepWAW[i];
        UINT32 tmp = 0;
        //printf ("distance %d\n", distance);

        if ( distance <= 2 )
          RegDepWAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 4 )
          RegDepWAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 8 )
          RegDepWAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 16 )
          RegDepWAW_recorder[tmp]++;
        else if ( ++tmp && distance <= 32 )
          RegDepWAW_recorder[tmp]++;
        else
          RegDepWAW_recorder[tmp]++;

      RegDepWAW[i] = 0;
      }

    }
#ifdef DEBUG_REGISTER 
    printf("\n");
#endif

  }
  return;

}
/************************* Register Depedency ************************/

/************************* Density Function ************************/
VOID DensityFunction(ADDRINT addr) {
  ADDRINT index;
  vector<ADDRINT>::iterator it;
  unsigned int depth, height, i;
  UINT64 pow = 1;
  bool f;
  depth = 0;

  /******************* block granularity **********************/
  index = (addr << 5);
  height = 0;
  f = false;
  for(it = block_stack.begin(); it != block_stack.end(); it++) {
    if (*it == index) {
      depth = block_stack.size() - height - 1;
      block_stack.erase(it);
      block_stack.push_back(index);
      f = true;
      break;
    }
    height++;
  }

  if (f) {
    for(i=1; i<11; i++) {
      pow *= 2;
      if (depth <= pow) {
        block_recorder[i-1]++;
        break;
      }
    }
    if (i == 11)
      block_recorder[i-1]++;
  }

  // not using it==end() because erase mess with stuff..
  if (!f) {
    block_stack.push_back(addr << 5);
    block_recorder[10]++;
    page_stack.push_back(addr << 11);
    page_recorder[10]++;
    return;
  }

  /******************* page granularity **********************/
  index = (addr << 11);
  height = 0;
  f = false;
  for(it = page_stack.begin(); it != page_stack.end(); it++) {
    if (*it == index) {
      depth = page_stack.size() - height - 1;
      page_stack.erase(it);
      page_stack.push_back(index);
      f = true;
      break;
    }
    height++;
  }

  if (f) {
    for(i=1; i<11; i++) {
      pow *= 2;
      if (depth <= pow) {
        page_recorder[i-1]++;
        return;
      }
    }
    if (i == 11)
      page_recorder[i-1]++;
    return;
  }
  assert(0);
}
/************************* Density Function ************************/

/************************* Cache Sim************************/
VOID CacheSim ( UINT32 v_bit, ADDRINT mem_read_addr, ADDRINT mem_read2_addr, ADDRINT mem_write_addr ) {

	current_cycle ++; // assume each instruction takes one cycle
	assert(v_bit<=7);
	//tot_reqs += (mem_read_v + mem_read2_v + mem_write_v);
	//printf("valid:\t%1X\n", v_bit);
	//printf("addr:\t%X %X %X\n", mem_read_addr, mem_read2_addr, mem_write_addr);

	if ( v_bit & 0x4) {
		mem_req_buffer.push_back(MemRequest(current_cycle, mem_read_addr, false));
		tot_reqs += 1;
	}

	if ( v_bit & 0x2 ) {
		mem_req_buffer.push_back(MemRequest(current_cycle, mem_read2_addr, false));
		tot_reqs += 1;
	}

	if ( v_bit & 0x1) {
		mem_req_buffer.push_back(MemRequest(current_cycle, mem_write_addr, true));
		tot_reqs += 1;
	}
	
	if ( !mem_req_buffer.empty() ) {
		enum opcode op = mem_req_buffer[0].is_store ? STORE : LOAD;
		cache_sim.cycle(mem_req_buffer[0].cycle, mem_req_buffer[0].address, op);	
		mem_req_buffer.erase(mem_req_buffer.begin());
	}
	

}
/************************* Cache Sim************************/

// implement all in the trace instrumentation level
VOID Trace(TRACE trace, VOID *v)
{

  //iterate BBLs
    for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
  {
    // count basic block size: this is static count
    BBL_size_container_static.push_back(BBL_NumIns(bbl));
    BBL_size_count.push_back(0);

    // count basic block size: this is dynamic count

	UINT32 current_BBL_index = BBL_index_global;
#ifdef OTHER_SIM_ENABLED
    BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)BBLInstrument, IARG_UINT32, current_BBL_index, IARG_UINT32, BBL_NumIns( bbl ), IARG_END);
#endif
	BBL_index_global ++;

    //iterate instructions
        for (INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins))
    {

      // Insert a call to docount before every instruction, no arguments are passed
    #ifdef PRINT_INSTRUCTION
      printf("***: %s\n",INS_Disassemble(ins).c_str());
    #endif

      // for register dependency
      int num_operands =INS_OperandCount(ins);
      UINT32 regs_read  = 0; // this is treated a bitset
      UINT32 regs_written = 0; // this is treated a bitset
      for (int i=0;i<num_operands;i++) {

        if ( INS_OperandIsReg(ins, i) ) {

          REG reg_tmp = INS_OperandReg( ins, i );

          // write regs
          if ( INS_OperandWritten ( ins, i ) ){

            UINT32 tmp = 0;
            switch ( reg_tmp ) {
            case REG_EAX:
#ifdef DEBUG_REGISTER
              printf( "\twrite to EAX\n" );
#endif
              tmp  = EE382M::EAX;
              break;
            case REG_EBX:
#ifdef DEBUG_REGISTER
              printf( "\twrite to EBX\n" );
#endif
              tmp  = EE382M::EBX;
              break;
            case REG_ECX:
#ifdef DEBUG_REGISTER
              printf( "\twrite to ECX\n" );
#endif
              tmp  = EE382M::ECX;
              break;
            case REG_EDX:
#ifdef DEBUG_REGISTER
              printf( "\twrite to EDX\n" );
#endif
              tmp  = EE382M::EDX;
              break;
            case REG_ESI:
#ifdef DEBUG_REGISTER
              printf( "\twrite to ESI\n" );
#endif
              tmp  = EE382M::ESI;
              break;
            case REG_EDI:
#ifdef DEBUG_REGISTER
              printf( "\twrite to EDI\n" );
#endif
              tmp  = EE382M::EDI;
              break;
            default:
              break;
            }
            
            if ( tmp > 0 )
              regs_written |= (0x1 << tmp);
          
          }

          // read regs
          if ( INS_OperandRead( ins, i ) ){
            UINT32 tmp = 0;

            switch ( reg_tmp ) {
            case REG_EAX:
#ifdef DEBUG_REGISTER
              printf( "\tread from EAX\n" );
#endif
              tmp  = EE382M::EAX;
              break;
            case REG_EBX:
#ifdef DEBUG_REGISTER
              printf( "\tread from EBX\n" );
#endif
              tmp  = EE382M::EBX;
              break;
            case REG_ECX:
#ifdef DEBUG_REGISTER
              printf( "\tread from ECX\n" );
#endif
              tmp  = EE382M::ECX;
              break;
            case REG_EDX:
#ifdef DEBUG_REGISTER
              printf( "\tread from EDX\n" );
#endif
              tmp  = EE382M::EDX;
              break;
            case REG_ESI:
#ifdef DEBUG_REGISTER
              printf( "\tread from ESI\n" );
#endif
              tmp  = EE382M::ESI;
              break;
            case REG_EDI:
#ifdef DEBUG_REGISTER
              printf( "\tread from EDI\n" );
#endif
              tmp  = EE382M::EDI;
              break;
            default:
              break;
            }

            if ( tmp > 0 )
              regs_read |= (0x1 << tmp);
          }

        }

      }

#ifdef OTHER_SIM_ENABLED
      INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)RegDependency , IARG_UINT32, regs_read, IARG_UINT32, regs_written, IARG_END);
#endif



      INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);

	  UINT32 mem_read_valid, mem_read2_valid, mem_write_valid; 
	  mem_read_valid = mem_read2_valid = mem_write_valid = 0;
      // update density function
      if (INS_IsMemoryRead( ins ) ) {
        // FIXME: manual says this function only works when IsMemoryRead..
#ifdef OTHER_SIM_ENABLED
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)DensityFunction, IARG_MEMORYREAD_EA, IARG_END);
#endif
	  	mem_read_valid = 1; 

		// cache sim
        if( INS_HasMemoryRead2(ins) ) {
#ifdef OTHER_SIM_ENABLED
          INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)DensityFunction, IARG_MEMORYREAD2_EA, IARG_END);
#endif
		  mem_read2_valid = 1;
        }
      }
      if (INS_IsMemoryWrite( ins ) ) {
#ifdef OTHER_SIM_ENABLED
          INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)DensityFunction, IARG_MEMORYWRITE_EA, IARG_END);
#endif
		  mem_write_valid = 1;
      }

	//printf("%d %d %d\n", mem_read_valid, mem_read2_valid, mem_write_valid);
	assert(mem_read_valid<=1);
	assert(mem_read2_valid<=1);
	assert(mem_write_valid<=1);
	UINT32 v_bit = (mem_read_valid << 2) | (mem_read2_valid << 1) | (mem_write_valid);
	
#ifdef CACHE_SIM_ENABLED
      if ( mem_read_valid && mem_read2_valid && mem_write_valid)
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_MEMORYREAD_EA, IARG_MEMORYREAD2_EA, IARG_MEMORYWRITE_EA, IARG_END);
	  else if ( mem_read_valid && mem_read2_valid && !mem_write_valid )
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_MEMORYREAD_EA, IARG_MEMORYREAD2_EA, IARG_UINT32, 0, IARG_END);
	  else if ( mem_read_valid && !mem_read2_valid && mem_write_valid )
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_MEMORYREAD_EA, IARG_UINT32, 0, IARG_MEMORYWRITE_EA, IARG_END);
	  else if ( !mem_read_valid && mem_read2_valid && mem_write_valid )
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_MEMORYREAD_EA, IARG_MEMORYREAD2_EA, IARG_UINT32, 0, IARG_END);
	  else if ( mem_read_valid && !mem_read2_valid && !mem_write_valid)
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_MEMORYREAD_EA, IARG_UINT32, 0, IARG_UINT32, 0, IARG_END);
	  else if ( !mem_read_valid && mem_read2_valid && !mem_write_valid)
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_UINT32, 0,IARG_MEMORYREAD2_EA, IARG_UINT32, 0, IARG_END);
	  else if ( !mem_read_valid && !mem_read2_valid && mem_write_valid)
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_UINT32, 0, IARG_UINT32, 0, IARG_MEMORYWRITE_EA, IARG_END);
	  else if ( !mem_read_valid && !mem_read2_valid && !mem_write_valid)
		  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CacheSim, IARG_UINT32, v_bit, IARG_UINT32, 0, IARG_UINT32, 0, IARG_UINT32, 0, IARG_END);
	  else 
	      assert(0);
#endif


#ifdef OTHER_SIM_ENABLED
      // Branch Behavior
      if ( (INS_IsDirectBranchOrCall(ins) || INS_IsIndirectBranchOrCall(ins) ||
            INS_IsBranchOrCall(ins) ) && !INS_IsCall(ins) ) {
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR) branch_behavior,
              IARG_INST_PTR, IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN, IARG_END);
      }
      INT32 IC= INS_Category ( ins );

      // judge if instruction is brach or call
      if ( IC ==  XED_CATEGORY_CALL || INS_IsIndirectBranchOrCall(ins) || 
         INS_IsBranchOrCall(ins)  || INS_IsDirectBranchOrCall(ins) ) {
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncBranchnCall, IARG_END);
    #ifdef PRINT_INSTRUCTION
        printf("\tbranch&call: %s\n",INS_Disassemble(ins).c_str());
    #endif
      }
      // judge if instruction is load
      else if (   INS_IsMemoryRead( ins ) || INS_IsStackRead ( ins ) || INS_IsIpRelRead ( ins ) ||
            INS_IsMemoryWrite( ins ) || INS_IsStackWrite( ins ) || INS_IsIpRelWrite( ins ) ) {
        bool is_load = false;

        if (  INS_IsMemoryRead( ins ) || INS_IsStackRead ( ins ) || INS_IsIpRelRead ( ins ) ) {
          INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncLoad, IARG_END);
    #ifdef PRINT_INSTRUCTION
          printf("\tload:%s\n",INS_Disassemble(ins).c_str());
    #endif
          is_load = true;
        }

        if (  INS_IsMemoryWrite( ins ) || INS_IsStackWrite( ins ) || INS_IsIpRelWrite( ins ) ) {
          INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncStore, IARG_END);
    #ifdef PRINT_INSTRUCTION
          printf("\tstore:%s\n",INS_Disassemble(ins).c_str());
    #endif
          if ( is_load ) {
            INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncLoadStore, IARG_END);
          }
        }
      }
      // judge if instruction is int or floating point
      else {

        int num_operands =INS_OperandCount(ins);
        bool has_reg = false;
        bool is_fp = false;

        for (int i=0;i<num_operands;i++) {
          //if (INS_OperandWritten(ins, i)) {
          //  num_dest++;
          //}
          if ( INS_OperandIsReg(ins, i) ) {
            has_reg = true;
            REG reg_tmp = INS_OperandReg( ins, i );
            if ( REG_is_fr_or_x87 ( reg_tmp ) || REG_is_any_x87 ( reg_tmp ) || REG_is_fr_for_get_context ( reg_tmp ) ) {
              INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncFP, IARG_END);
              is_fp = true;
    #ifdef PRINT_INSTRUCTION
              printf("\tfp: %s\n",INS_Disassemble(ins).c_str());
    #endif
              break;
            }
          }
        }

        if ( has_reg && ! is_fp ) {
          INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncINT, IARG_END);

    #ifdef PRINT_INSTRUCTION
          printf("\tint: %s\n",INS_Disassemble(ins).c_str());
    #endif
        }

    //    for (int i=0;i<num_operands;i++) {
    //      if ( INS_OperandIsMemory ( ins, i ) ){
    //        if ( INS_OperandRead ( ins, i ) ){
    //          INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncLoad, IARG_END);
    //#ifdef PRINT_INSTRUCTION
    //          printf("\tload: %s\n",INS_Disassemble(ins).c_str());
    //#endif
    //          break;
    //        }
    //      }
    //    }
    //
    //    for (int i=0;i<num_operands;i++) {
    //      if ( INS_OperandIsMemory ( ins, i ) ){
    //        if ( INS_OperandWritten ( ins, i ) ){
    //          INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)IncStore, IARG_END);
    //#ifdef PRINT_INSTRUCTION
    //          printf("\tstore: %s\n",INS_Disassemble(ins).c_str());
    //#endif
    //          break;
    //        }
    //      
    //      } 
    //    }


      }
#endif
    }


  }
}

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
    "o", "inscount.out", "specify output file name");

// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
    // Write to a file since cout and cerr maybe closed by the application
    OutFile.setf(ios::showbase);

	//necessary calculation
#ifdef OTHER_SIM_ENABLED
	double bbl_size_avg = 0;
	double tot_bb_count = 0;
	for ( UINT64 i = 0; i < BBL_size_container_static.size(); i ++ ) {
		bbl_size_avg += (BBL_size_container_static[i] * BBL_size_count[i]);
		tot_bb_count += BBL_size_count[i];
	}
	bbl_size_avg /= tot_bb_count;

	double bbl_size_dev= 0;
	for ( UINT64 i = 0; i < BBL_size_container_static.size(); i ++ ) {
		bbl_size_dev += ( pow ( (BBL_size_container_static[i] - bbl_size_avg), 2) * BBL_size_count[i] );
	}
	bbl_size_dev /= tot_bb_count;
	bbl_size_dev = pow ( bbl_size_dev, 0.5 );
#endif

//=======================
//	double bbl_size_avg_tmp = 0;
//	double tot_bb_count_tmp = BBL_size_container_dynamic.size();
//	for ( UINT64 i = 0; i < BBL_size_container_dynamic.size(); i ++ ) {
//		bbl_size_avg_tmp += (BBL_size_container_dynamic[i]); 
//	}
//	bbl_size_avg_tmp /= tot_bb_count_tmp;
//
//	double bbl_size_dev_tmp= 0;
//	for ( UINT64 i = 0; i < BBL_size_container_dynamic.size(); i ++ ) {
//		bbl_size_dev_tmp += ( pow ( (BBL_size_container_dynamic[i] - bbl_size_avg_tmp), 2) );
//	}
//	bbl_size_dev_tmp = pow ( bbl_size_dev_tmp, 0.5 );
//	bbl_size_dev_tmp /= tot_bb_count_tmp;
//	// Average Basic Block Size
//    OutFile<<"here\n";
//    OutFile<<tot_bb_count<<"\t";
//    OutFile<<tot_bb_count_tmp<<"\t";
//    OutFile<<bbl_size_avg_tmp<<"\t";
//	// Basic Block Size Deviation
//    OutFile<<bbl_size_dev_tmp<<"\n";
//=======================

#ifdef OTHER_SIM_ENABLED
	// Instruction Count
	OutFile<<instr_mix[EE382M::TOTAL]<<"\t";
	// Percent Int
	OutFile<<(float)instr_mix[EE382M::INT]/instr_mix[EE382M::TOTAL]<<"\t";
	// Percent FP
	OutFile<<(float)instr_mix[EE382M::FP]/instr_mix[EE382M::TOTAL]<<"\t";
	// Percent Load
	OutFile<<(float)instr_mix[EE382M::LOAD]/instr_mix[EE382M::TOTAL]<<"\t";
	// Percent Store
	OutFile<<(float)instr_mix[EE382M::STORE]/instr_mix[EE382M::TOTAL]<<"\t";
	// Percent Branch
	OutFile<<(float)instr_mix[EE382M::BRANCHnCALL]/instr_mix[EE382M::TOTAL]<<"\n";

	// Percent Branch Taken
	OutFile<<(float)instr_mix[EE382M::BRANCH_T]/instr_mix[EE382M::BRANCH]<<"\t";
	// Percent Forward Branch Taken
	OutFile<<(float)instr_mix[EE382M::FORWARD_BR_T]/
		instr_mix[EE382M::FORWARD_BR]<<"\n";
	
	// Average Basic Block Size
    OutFile<<bbl_size_avg<<"\t";
	// Basic Block Size Deviation
    OutFile<<bbl_size_dev<<"\n";

	// Read after Write Dependecy
	for ( int i = 0; i < 6; i ++) {
	  OutFile<<RegDepRAW_recorder[i];
	  if (i != 5) OutFile<<"\t";
	} 
	OutFile<<"\n";
	// Write after Write Dependecy
	for ( int i = 0; i < 6; i ++) {
	  OutFile<<RegDepWAW_recorder[i];
	  if (i != 5) OutFile<<"\t";
	} 
	OutFile<<"\n";
	// Write after Read Dependecy
	for ( int i = 0; i < 6; i ++) {
	  OutFile<<RegDepWAR_recorder[i];
	  if (i != 5) OutFile<<"\t";
	} 
	OutFile<<"\n";

	// Cache Block Density Function
	for(int i=0; i<11; i++) {
		OutFile << block_recorder[i]; 
		if (i != 11) OutFile<<"\t";
	}
	OutFile << endl;
	// Page Density Function
	for(int i=0; i<11; i++) {
		OutFile << page_recorder[i];
		if (i != 11) OutFile<<"\t";
	}
	OutFile << endl;
#endif

	// L1 miss rate

#ifdef CACHE_SIM_ENABLED
	OutFile << ((double)(cache_sim.L1->m_num_misses))/((double)(cache_sim.L1->m_num_accesses)) << "\n"; 
#endif


// no use
#if 0

    for(int i=EE382M::INT; i<EE382M::InstructionMixCount; i++)
      OutFile << i<<" "<<InsString[i]<<" "<<instr_mix[i]<< endl;

    OutFile << "\n--------------------------"<< endl;
    OutFile << "BBL static stats:" << endl;
    OutFile << "BBL counts " << BBL_size_container_static.size() << endl;

  bbl_size_avg = 0;
  for ( UINT64 i = 0; i < BBL_size_container_static.size(); i ++ ) {
    bbl_size_avg += BBL_size_container_static[i];
  }
  bbl_size_avg /= BBL_size_container_static.size();

  bbl_size_dev= 0;
  for ( UINT64 i = 0; i < BBL_size_container_static.size(); i ++ ) {
    bbl_size_dev += pow ( (BBL_size_container_static[i] - bbl_size_avg), 2);
  }

  bbl_size_dev = pow ( bbl_size_dev, 0.5 );
  bbl_size_dev /= BBL_size_container_static.size();
    OutFile << "BBL size average " << bbl_size_avg << endl;
    OutFile << "BBL size standard deviation " << bbl_size_dev << endl;


    OutFile << "\n--------------------------"<< endl;
    OutFile << "BBL dynamic stats:" << endl;
    OutFile << "BBL counts " << BBL_size_container_dynamic.size() << endl;
  bbl_size_avg = 0;
  for ( UINT64 i = 0; i < BBL_size_container_dynamic.size(); i ++ ) {
    bbl_size_avg += BBL_size_container_dynamic[i];
  }
  bbl_size_avg /= BBL_size_container_dynamic.size();

  bbl_size_dev= 0;
  for ( UINT64 i = 0; i < BBL_size_container_dynamic.size(); i ++ ) {
    bbl_size_dev += pow ( (BBL_size_container_dynamic[i] - bbl_size_avg), 2);
  }
  bbl_size_dev = pow ( bbl_size_dev, 0.5 );
  bbl_size_dev /= BBL_size_container_dynamic.size();
    OutFile << "BBL size average " << bbl_size_avg << endl;
    OutFile << "BBL size standard deviation " << bbl_size_dev << endl;


    OutFile << "\n--------------------------"<< endl;
    OutFile << "Register Depdency: " << endl;

    OutFile << "RAW: " << endl;

  for ( int i = 0; i < 6; i ++) {
    int tmp = ( 2 << (i));
    OutFile << " < " << tmp << ": " << RegDepRAW_recorder[i] << endl;
  } 

    OutFile << "WAR: " << endl;

  for ( int i = 0; i < 6; i ++) {
    int tmp = ( 2 << (i));
    OutFile << " < " << tmp << ": " << RegDepWAR_recorder[i] << endl;
  } 

    OutFile << "WAW: " << endl;

  for ( int i = 0; i < 6; i ++) {
    int tmp = ( 2 << (i));
    OutFile << " < " << tmp << ": " << RegDepWAW_recorder[i] << endl;
  } 

    OutFile << "\n--------------------------"<< endl;
    OutFile << "Density Function: " << endl;
    OutFile << " block granularity: "<<endl;
  for(int i=0; i<11; i++)
    OutFile << block_recorder[i] << "\t";
  OutFile << endl;

    OutFile << " page granularity: "<<endl;
	for(int i=0; i<11; i++)
		OutFile << page_recorder[i] << "\t";
	OutFile << endl;

	

	// cache sim
    OutFile << "\n--------------------------"<< endl;
    OutFile << "\ttotal requests sent " << tot_reqs << endl;
	cache_sim.finish();
	cache_sim.print_stats(OutFile);
#endif

    OutFile.close();
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool counts the number of dynamic instructions executed" << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */
/*   argc, argv are the entire command line: pin -t <toolname> -- ...    */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    OutFile.open(KnobOutputFile.Value().c_str());

#ifdef CACHE_SIM_ENABLED
	cache_sim.init();
#endif

    // Register Instruction to be called to instrument instructions
    TRACE_AddInstrumentFunction(Trace, 0);
    //INS_AddInstrumentFunction(Instruction, 0);

    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);

    // Start the program, never returns
    PIN_StartProgram();

    return 0;
}
